package com.mixsmart.core.xml.sqlmap;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import com.mixsmart.core.parser.IParser;
import com.mixsmart.core.xml.sqlmap.bean.SQLMapFile;
import com.mixsmart.core.xml.sqlmap.bean.SqlUnit;
import com.mixsmart.core.xml.validator.SqlMapValidator;
import com.mixsmart.utils.CollectionUtils;
import com.mixsmart.utils.StringUtils;

/**
 * 解析sqlmap XML文件
 * @author lmq
 * @version 1.0
 * @since JDK版本大于等于1.6
 * 2016年1月20日
 */
public class SqlMapParser implements IParser {

	private static Logger logger = Logger.getLogger(SqlMapParser.class);
	
	private String sqlMapPath;
	
	private SqlMapValidator sqlMapvalidator;
	
	private List<SQLMapFile> sqlMapFiles;
	
	public SqlMapParser(String sqlMapPath) {
		this.sqlMapPath = sqlMapPath;
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public <E> E parser() {
		sqlMapFiles = new ArrayList<SQLMapFile>();
		try {
			this.parser(sqlMapPath, 0);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return (E) sqlMapFiles;
	}
	
	/**
	 * 解析
	 * @param sqlMaps 解析结果存放对象
	 * @param sqlMapFile 文件
	 * @throws Exception
	 */
	private void parser(String xmlPath, int count) throws Exception {
		if(StringUtils.isNotEmpty(xmlPath)) {
			InputStream inputStream = this.getClass().getResourceAsStream("/"+xmlPath);
			if(null == inputStream ) {
				logger.error("["+xmlPath+"]文件不存在...");
				throw new Exception("["+xmlPath+"]文件不存在...");
			}
			if(null == sqlMapvalidator) {
				sqlMapvalidator = SqlMapValidator.getInstance(xmlPath);
			} else {
				sqlMapvalidator.setXmlFile(xmlPath);
			}
			if(!sqlMapvalidator.validator()) {
				logger.error("["+xmlPath+"]文件验证失败...");
				throw new Exception("["+xmlPath+"]文件验证失败");
			}
			try {
				SQLMapFile sqlMapFile = null;
				if(!this.sqlMapFiles.isEmpty()) {
					sqlMapFile = this.sqlMapFiles.get(count);
				}
				if(null == sqlMapFile) {
					sqlMapFile = new SQLMapFile();
					URL url = this.getClass().getResource("/"+xmlPath);
					if(null != url && !url.toURI().isOpaque()){
						File file = (null != url) ? new File(url.toURI()) : null;
						sqlMapFile.setFileModifyTime(file.lastModified());
					}
					sqlMapFile.setFilePath(xmlPath);
					this.sqlMapFiles.add(sqlMapFile);
				}
				SAXReader saxReader = new SAXReader();
				Document document = saxReader.read(inputStream);
				Element root = document.getRootElement();
				sqlMapFile.setNamespaces(root.attributeValue("namespaces"));
				Map<String,SqlUnit> sqlMapValues = new HashMap<String, SqlUnit>();
				for(Iterator<?> i = root.elementIterator(); i.hasNext();) {
					Element element = (Element) i.next();
					if("include".equals(element.getName())) {
						String includeFilePath = element.attributeValue("file");
						if(StringUtils.isNotEmpty(includeFilePath)) {
							if(includeFilePath.startsWith("./")) {
								String parentDir = xmlPath.substring(0,xmlPath.lastIndexOf("/")+1);
								includeFilePath = includeFilePath.replace("./", parentDir);
							} 
							String checkFilePath = includeFilePath;
							if(!includeFilePath.startsWith("/")) {
								checkFilePath = "/"+includeFilePath;
							}
							URL url = this.getClass().getResource(checkFilePath);
							if(null != url) {
								URI uri = url.toURI();
								boolean isExist = true;
								SQLMapFile sqlMapFileTmp = new SQLMapFile();
								sqlMapFileTmp.setFilePath(includeFilePath);
								if(!uri.isOpaque()){
									File file = (null != url) ? new File(url.toURI()) : null;
									if(null != file && file.exists()) {
										sqlMapFileTmp.setFileModifyTime(file.lastModified());
									} else {
										isExist = false;
									}
								}
								if(isExist) {
									if(!isRepeat(includeFilePath)) { 
										this.sqlMapFiles.add(sqlMapFileTmp);
									}
								}
							} else {
								logger.error("无法找到["+checkFilePath+"]资源文件---");
							}
						}//if
					} else {
						String name = element.attributeValue("name");
						boolean isFilter = Boolean.parseBoolean(element.attributeValue("isFilter"));
						String sql = element.elementText("sql");
						if(StringUtils.isNotEmpty(name) && StringUtils.isNotEmpty(sql)) 
							sqlMapValues.put(name, new SqlUnit(name, isFilter, StringUtils.removeMultiSpace(sql)));
					}
				}
				if(sqlMapValues.size()>0) {
					sqlMapFile.addSqlMaps(sqlMapValues);
					sqlMapValues=null;
				}
				root = null;
				logger.info("初始化sql配置文件["+xmlPath+"]--[成功]");
				document.clearContent();
				saxReader = null;
				document = null;
			} catch (DocumentException e) {
				logger.error("初始化sql配置文件["+xmlPath+"]--[失败]");
				throw new Exception(e.getMessage());
			} catch (Exception e) {
				logger.error("初始化sql配置文件["+xmlPath+"]--[失败]");
				throw new Exception(e.getMessage());
			} finally {
				try {
					if(null != inputStream)
						inputStream.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		count++;
		if(CollectionUtils.isNotEmpty(this.sqlMapFiles) && this.sqlMapFiles.size()>count) {
			String filePath = sqlMapFiles.get(count).getFilePath();
			this.parser(filePath, count);
		}
	}
	
	
	/**
	 * 判断是否有重复的
	 * @param filePath 文件路径
	 * @return 有重复返回：true，否则返回：false
	 */
	private boolean isRepeat(String filePath) {
		boolean is = false;
		if(CollectionUtils.isNotEmpty(this.sqlMapFiles) && StringUtils.isNotEmpty(filePath)) {
			for (SQLMapFile sqlMapFile : this.sqlMapFiles) {
				if(filePath.equals(sqlMapFile.getFilePath())) {
					is = true;
					break;
				}
			}
		}
		return is;
	}
	
	/**
	 * 重新载入修改过的SQLMAP文件
	 */
	public void reloadModifySqlMap() {
		if(CollectionUtils.isNotEmpty(sqlMapFiles) && !sqlMapFiles.isEmpty()) {
			int count = 0;
			for (SQLMapFile sqlMapFile : sqlMapFiles) {
				URL url = this.getClass().getResource("/"+sqlMapFile.getFilePath());
				try {
					if(null != url && !url.toURI().isOpaque()) {
						File file = (null != url) ? new File(url.toURI()) : null;
						if(null != file && file.exists() && 
								(file.lastModified() > sqlMapFile.getFileModifyTime())) {
							sqlMapFile.setFileModifyTime(file.lastModified());
							logger.debug("["+file.getName()+"]文件内容有改动，重新加载...");
							this.parser(sqlMapFile.getFilePath(), count);
							logger.debug("["+file.getName()+"]文件重新加载[完成]...");
						}
					}
				} catch (URISyntaxException e) {
					e.printStackTrace();
				} catch (Exception e) {
					e.printStackTrace();
				}
				count++;
			}//for
		}//if
	} 
}
